package com.ht.modules.system.service.impl;

import com.ht.modules.system.entity.DbBackFile;
import com.ht.modules.system.service.IDbBackService;
import com.ht.modules.system.util.ZipMultiFile;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import com.ht.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 *  数据库备份
 * @Author:
 * @Date: 2021-11-12
 * @Version: V1.0
 */
@Service
@Slf4j
public class DbBackServiceImpl implements IDbBackService {

    @Autowired
    Environment env;
    @Autowired
    private ZipMultiFile zipMultiFile;
    @Value(value = "${dbBack.db-name}")
    private String dbName;
    @Value(value = "${dbBack.mysql-back-url}")
    private String mysqlBackUrl;
    @Value(value = "${dbBack.is-docker-install}")
    private Boolean isDockerInstall;
    @Value(value = "${dbBack.mysql-back-docker-url}")
    private String mysqlBackDockerUrl;


    @Override
    public List<DbBackFile> dbBackFileList() {
        // 获取备份保留路径
        File backDirectory = new File(mysqlBackUrl);
        if (!backDirectory.exists() || !backDirectory.isDirectory()) {
            backDirectory.mkdirs();
        }
        File[] backFiles = backDirectory.listFiles();
        List<DbBackFile> resultList = new ArrayList<>();
        BigDecimal toMb = new BigDecimal(1024 * 1024);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        for (File backFile : backFiles) {
            if (backFile.isFile()) {
                DbBackFile dbBackFile = new DbBackFile();
                dbBackFile.setId(backFile.getName());
                dbBackFile.setFileName(backFile.getName());
                dbBackFile.setFileUrl(backFile.getAbsolutePath());
                String str = DateUtils.formatTime(backFile.lastModified());
                Date fileTime = DateUtils.str2Date(str, sdf);
                dbBackFile.setFileTime(fileTime);
                dbBackFile.setFileSize(new BigDecimal(backFile.length()).divide(toMb, 2, BigDecimal.ROUND_HALF_UP));
                resultList.add(dbBackFile);
            }
        }
        resultList = resultList.stream().sorted(new Comparator<DbBackFile>() {
            @Override
            public int compare(DbBackFile o1, DbBackFile o2) {
                return o2.getFileName().compareTo(o1.getFileName());
            }
        }).collect(Collectors.toList());
        return resultList;
    }

    @Override
    public void createDbBack() {
        if (Strings.isBlank(mysqlBackUrl)) {
            throw new RuntimeException("缺少数据库备份配置信息！");
        }
        if (null == isDockerInstall) {
            throw new RuntimeException("缺少数据库备份配置信息！");
        }
        if (isDockerInstall) {
            if (Strings.isBlank(mysqlBackDockerUrl)) {
                throw new RuntimeException("缺少数据库备份配置信息！");
            }
        }

        SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmm");
        Date date = new Date();
        String osName = System.getProperty("os.name").toLowerCase().replaceAll("\\s", "");
        String database = dbName;
        String fileName = database + sdf.format(date) + ".sql";
        String fileUrl = "";
        String backFileUrl = mysqlBackUrl + File.separator + fileName;

        File zipFile = new File(mysqlBackUrl + File.separator + database + sdf.format(date) + ".zip");
        File fileParent = new File(zipFile.getParent());
        if (!fileParent.exists()) {
            fileParent.mkdirs();
        }
        if (zipFile.exists()) {
            zipFile.delete();
        }
        List<File> fileList = new ArrayList<File>();
        fileUrl = mysqlBackUrl + File.separator + fileName;
        if (isDockerInstall) {
            fileUrl = mysqlBackDockerUrl + File.separator + fileName;
        }

        File oldBackFile = new File(backFileUrl);
        if (oldBackFile.exists()) {
            oldBackFile.delete();
        }
        String user = env.getProperty("spring.datasource.dynamic.datasource.master.username");
        String password = env.getProperty("spring.datasource.dynamic.datasource.master.password");
        StringBuilder cmd = new StringBuilder();

        // DOCKER容器备份命令：docker exec mysql sh -c 'exec mysqldump pro -uroot -pmysql-XXXX > /var/lib/mysql/mysql-files/pro.sql'
        // window备份命令： mysqldump pro -uroot -pXXXX > D:\pro.sql
        if (osName.contains("win")) {
            // Windows系统
            cmd.append(" mysqldump ").append(database).append(" -u").append(user).append(" -p").append(password).append(" >").append(fileUrl);
        } else if (osName.contains("nix") || osName.contains("nux")) {
            // Linux操作系统
            cmd.append("docker exec mysql sh -c 'exec mysqldump ").append(database).append(" -u").append(user).append(" -p").append(password).append(" >").append(fileUrl).append("'");
        }
        try {
            String[] sysCmd;
            if (osName.contains("win")) {
                // Windows系统
                sysCmd = new String[]{"cmd", "/c", cmd.toString()};
            } else if (osName.contains("nix") || osName.contains("nux")) {
                // Linux操作系统
                sysCmd = new String[]{"/bin/sh", "-c", cmd.toString()};
            } else {
                throw new RuntimeException("当前为未知系统！");
            }
            // 创建一个线程去执行
            Process proc = Runtime.getRuntime().exec(sysCmd);
            proc.waitFor();
            File backFile = new File(backFileUrl);
            if (!backFile.exists()) {
                throw new RuntimeException("备份数据库失败");
            } else {
                fileList.add(backFile);
            }
            proc.destroy();

            // 将文件打包
            zipMultiFile.zipFiles(fileList, zipFile);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("备份数据库失败");
        } finally {
            for (File eMap : fileList) {
                eMap.delete();
            }
        }
    }

    @Override
    public void downDbBackFile(String fileName, HttpServletResponse response) {
        if (Strings.isBlank(fileName)) {
            return;
        }
        // 其余处理略
        BufferedInputStream inputStream = null;
        BufferedOutputStream outputStream = null;
        File file = null;
        try {
            String filePath = mysqlBackUrl + File.separator + fileName;
            file = new File(filePath);
            if (!file.exists()) {
                response.setStatus(404);
                throw new RuntimeException("文件[" + filePath + "]不存在..");
            }
            response.setContentType("application/force-download");// 设置强制下载不打开
            response.addHeader("Content-Disposition", "attachment;fileName=" +
                    new String(file.getName().getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
            response.setContentLengthLong(file.length());
            inputStream = new BufferedInputStream(Files.newInputStream(Paths.get(filePath)));
            outputStream = new BufferedOutputStream(response.getOutputStream());
            byte[] buf = new byte[1024];
            int len;
            while ((len = inputStream.read(buf)) > 0) {
                outputStream.write(buf, 0, len);
            }
            response.flushBuffer();
        } catch (IOException e) {
            response.setStatus(404);
        } finally {
            try {
                if (null != inputStream) {
                    inputStream.close();
                }
            } catch (IOException e) {
                log.error("读文件流关闭失败：", e.getMessage(), inputStream, e);
            }
            try {
                if (null != outputStream) {
                    outputStream.close();
                }
            } catch (IOException e) {
                log.error("写文件流关闭失败：", e.getMessage(), outputStream, e);
            }
        }
    }

    @Override
    public void deleteDbBackFile(String fileName) {
        if (Strings.isBlank(fileName)) {
            throw new RuntimeException("参数非法！");
        }
        // 获取备份保留路径
        String filePath = mysqlBackUrl + File.separator + fileName;
        File backFile = new File(filePath);
        if (backFile.exists()) {
            backFile.delete();
        } else {
            throw new RuntimeException("未找到备份文件");
        }
    }

}
